#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <event2/event.h>
#include <event2/http.h>
#include <event2/buffer.h>
#include <event2/util.h>
#include <event2/keyvalq_struct.h>

#include "main.h"
#include "log.h"
#include "daemon.h"
#include "xmalloc.h"
#ifdef ENABLE_PTHREADS
# include "thread.h"
#endif
#include "sighandler.h"
#include "string.h"
#include "dict.h"
#include "scanner.h"
#include "reader.h"
#include "db.h"
#include "tag.h"
#include "var.h"
#include "template.h"
#include "httpd.h"
#include "url.h"
#include "static_file.h"
#include "session.h"

#ifdef ENABLE_DEBUG
# include <mcheck.h>
#endif


static const char *mtag = "ncblog";


static void
nc_init(const char *__doc_dir, char *__static_dir, const char *static_exts[], const char *mimes[])
{
  init_var();
  strncpy(doc_dir, __doc_dir, MAXPATHLEN);
  if ( !init_html(mtag) ) {
    E("absolutly nothing interesting found in %s, abort ..", __doc_dir);
    exit(EXIT_FAILURE);
  }
  check_var_pdata(pdata);
  if ( !init_static_files(__static_dir, static_exts, mimes) ) {
    W("no static files found in %s", __static_dir);
  }
  preinit_templates();
}

static void
nc_free(void)
{
  url_free();
  free_templates();
  free_static_files();
  free_all_parse_data(pdata);
  free_var();
}

/* handler functions: called after a dynamic document is parsed or static file is rdy */
static int
httpd_loop_handler(struct request *fr, string_t *output, dict_t *hdr, ck_map_t cookies)
{
  httpd_default_header((struct evhttp_request *)fr->__unused, hdr, cookies);
  httpd_out((struct evhttp_request *)fr->__unused, output, 200, "OK");
  return (0);
}

static void
print_requests(void)
{
  I("served %lu requests", requests_processed());
}

/* global signal handler callback */
static void
sighandler(int signum)
{
  switch (signum) {
    case SIGTERM:
    case SIGINT:
      W2("shutdown ..");
        httpd_exit();
      break;
    case SIGUSR1:
      print_requests();
      xmem_usage();
      break;
  }
}

int
main(int argc __attribute__((__unused__)), char **argv __attribute__((__unused__)))
{
#if defined(PACKAGE_NAME) && defined(ARGV0_MAXLEN)
  if (argc > 0)
  {
    memset(argv[0], 0, ARGV0_MAXLEN);
    /* change the proc name to ncblog without cmdline arguments */
    strncpy(argv[0], PACKAGE_NAME, ARGV0_MAXLEN);
  }
#endif

#ifdef ENABLE_DEBUG
  mtrace();
#endif

  init_sighandler(sighandler); // init global sighandler
  set_log_opts(log_stderr, log_debug);
#ifdef NC_VERSION_STRING
  I("running %s", NC_VERSION_STRING);
#endif
  init_url_handler();

  /* set database argument
   * this could be a path to a directory (for sqlite3)
   * or a url like mysql://127.0.0.1:4444 (future implementation)
   */
  if ( db_set_arg("www/db") ) goto error;

  /* set the template root */
  if ( set_template_dir("www/tmpl") ) goto error;

  /* file extensions && mime types are necessary for static files
   * because the client browser will need it to display the static file correctly
   */
  const char *exts[] = { "png", "jpg", "jpeg", "ico", "css", "js", NULL };
  const char *mimes[] = { "image/png", "image/jpg", "image/jpeg", "image/ico", "text/css", "text/javascript", NULL };

  /* do the parsing stuff
   * read/store/parse (static/dynamic) files into your memory
   */
  nc_init("www/html", "www/html", exts, mimes);

  /* fork into the background */
  if ( daemonize() ) goto error;
  log_init(NULL);
  if (db_preinit() != DB_ERR_OK) goto error;

  /* preinit builtins */
  call_vpreinit_cbs();
  accept_signals = true;

  /* init session manager */
  init_sessionmgr();

  /* init threaded env */
#ifdef ENABLE_PTHREADS
  init_threads(4);
#endif

  set_request_handler(httpd_loop_handler);
  if (!httpd_run("0.0.0.0", 8000)) goto error;

#ifdef ENABLE_PTHREADS
  free_threads();
#endif

  /* stop/free the session manager */
  free_sessionmgr();

  /* call all (builtin) shutdown callbacks */
  call_vshutdown_cbs();
  /* free all db data (important for sqlite3) */
  db_shutdown();
  /* free all parsed/static/etc stuff */
  nc_free();
  /* print a pretty request/memory usage message */
  print_requests();
  ncmem_usage();
  /* end log stream, close log fd's if needed */
  log_end();

  return (EXIT_SUCCESS);
error:
  nc_free();
  return (EXIT_FAILURE);
}
